package com.bjy.qa.util.security;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 自定义密名账户验证类，它负责根据 Authentication 对象、访问主体的权限信息以及资源的安全配置，决定主体是否有权限访问资源
 */
@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {
    /**
     * 判断当前登录的用户是否具备当前请求URL所需要的角色信息。如果不具备，就抛出 AccessDeniedException 异常，否则不做任何事即可
     * @param authentication 当前登录用户的信息
     * @param object FilterInvocation对象，可以获取当前请求对象
     * @param configAttributes FilterInvocationSecurityMetadataSource 中的 getAttributes() 方法的返回值，即当前请求URL所需的角色
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        Collection<? extends GrantedAuthority> auths = authentication.getAuthorities(); // 获取当前登录用户的角色信息（之前在 JwtAuthenticationTokenFilter 中存入的用户角色信息）

        // 遍历当前请求 URL 所需要的角色信息（这里角色以 |admin|user| 形式放回所以只判断一次）
        for (ConfigAttribute configAttribute : configAttributes) {
            if ("ROLE_ANONYMOUS".equals(configAttribute.getAttribute())) {
                return;
            }

            // 遍历当前登录用户的角色信息，判断是否具备当前请求 URL 所需要的角色信息
            for (GrantedAuthority authority : auths) {
                if (configAttribute.getAttribute().contains("|" + authority.getAuthority() + "|")) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("拒绝访问 - 未授权");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}